package top.ieei.demo.tftp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Hashtable;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * TFTP服务套接字封装<br>
 * @author xiecongshu
 */
public class ServerSocket implements Runnable
{
    /**
     * 日志
     */
    private static final Log LOG = LogFactory.getLog(ServerSocket.class);
    
    /**
     * TFTP请求连接，以IP和端口共同标记
     */
    private Hashtable<String, RequestHandler> tftpConnects;
    
    /**
     * TFTP工作池大小
     */
    private int poolSize;
    
    /**
     * TFTP工作池
     */
    private TftpWorkPool workers;
    
    /**
     * 数据报套接字
     */
    private DatagramSocket serverSocket;
    
    /**
     * TFTP服务监听端口
     */
    private int serverPort;
    
    /**
     * 是否中断服务
     */
    private boolean abort = false;
    
    /**
     * <默认构造函数>
     * @param serverPort TFTP服务监听端口
     * @param poolSize TFTP工作池大小
     */
    public ServerSocket(int serverPort, int poolSize)
    {
        this.serverPort = serverPort;
        this.poolSize = poolSize;
        tftpConnects = new Hashtable<String, RequestHandler>();
        LOG.info("TFTP Server creating new pool of " + poolSize
                + " worker threads");
        workers = new TftpWorkPool(poolSize, tftpConnects);
    }
    
    /**
     * 设置TFTP工作池大小
     * @param poolSize void
     */
    public void setPoolSize(int poolSize)
    {
        this.poolSize = poolSize;
        workers.resize(poolSize);
    }
    
    /**
     * 获取TFTP工作池大小
     * @return int
     */
    public int getPoolSize()
    {
        return poolSize;
    }
    
    /**
     * TFTP 服务监听任务
     */
    public void run()
    {
        // 缓冲大小
        int bufSize = 528;
        
        // 请求客户端地址
        InetAddress clientAddress;
        
        // 请求客户端端口
        int clientPort;
        
        //未中断的情况下，一直监听请求
        while (!abort)
        {
            byte[] buffer = new byte[bufSize];
            DatagramPacket packet = new DatagramPacket(buffer, bufSize);
            try
            {
                serverSocket.receive(packet);
            }
            catch (IOException ioe)
            {
                continue;
            }
            clientAddress = packet.getAddress();
            clientPort = packet.getPort();
            LOG.info("TFTP server received request for file from "
                    + clientAddress.toString() + ":" + clientPort);
            
            // 拷贝UDP包数据到tftpBytes
            byte[] tftpBytes = new byte[packet.getLength()];
            System.arraycopy(packet.getData(),
                    packet.getOffset(),
                    tftpBytes,
                    0,
                    packet.getLength());
            
            TftpRequest request;
            int opCode = TftpPacket.fetchOpCode(tftpBytes);
            try
            {
                switch (opCode)
                {
                    case ReadRequest.OPCODE:
                        request = new ReadRequest(tftpBytes);
                        break;
                    case WriteRequest.OPCODE:
                        request = new WriteRequest(tftpBytes);
                        break;
                    default:
                        continue;
                }
            }
            catch (TftpException e)
            {
                LOG.error("TFTP server received request from "
                        + clientAddress.toString() + ":" + clientPort + ". "
                        + e.getErrorMessage());
                continue;
            }
            
            // 请求文件名检查，如果不合法丢弃该请求
            if (request.getFileName() == null
                    || request.getFileName().length() == 0)
            {
                continue;
            }
            workers.addToWorkPool(request, clientAddress, clientPort);
        }
        LOG.info("TFTP server is stoped.");
    }
    
    /**
     * 停止TFTP服务
     */
    public void stop()
    {
        workers.resize(0);
        abort = true;
        if (null != serverSocket)
        {
            serverSocket.close();
        }
    }
    
    /**
     * 开启TFTP服务
     *
     * @throws SocketException void
     */
    public void start() throws SocketException
    {
        serverSocket = new DatagramSocket(serverPort);
        serverSocket.setSoTimeout(1000);
        new Thread(this).start();
        LOG.info("TFTP server is started.");
    }
    
}
